package com.sds.replicator.helpers;


import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.sds.replicator.transformers.Transformer;
import org.apache.commons.io.IOUtils;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

public class ResponseWrapper {

    private static String CHAR_SET = "charset=";
    private static String LOCATION = "Location";
    private static String COOKIES = "set-cookie";
    private static Set<String> RESERVED_TOKENS = Sets.newHashSet(
            "domain",
            "path",
            "expires"
    );
    protected static Logger LOGGER = Logger.getLogger(ResponseWrapper.class.getName());

    protected ResponseWrapper() {
    }

    private String localPath;
    private int statusCode;
    private String contentType;
    private String encoding;
    private Map<String, String> headers = Maps.newHashMap();
    private Map<String, String> cookies = Maps.newHashMap();
    private byte[] content = null;

    public static ResponseWrapper wrap(String localPath, HttpURLConnection connection) throws IOException {
        ResponseWrapper responseWrapper = new ResponseWrapper();
        responseWrapper.localPath = localPath;
        responseWrapper.statusCode = connection.getResponseCode();
        responseWrapper.contentType = connection.getContentType();
        responseWrapper.encoding = getEncoding(connection.getContentType());
        responseWrapper.content = IOUtils.toByteArray(connection.getInputStream());
        LOGGER.info(String.format("[StatusCode=%d;ContentType='%s';Encoding='%s']", responseWrapper.statusCode, responseWrapper.contentType, responseWrapper.encoding));
        connection.getInputStream().close();
        for (Iterator i = connection.getHeaderFields().entrySet().iterator(); i.hasNext(); ) {
            Map.Entry mapEntry = (Map.Entry) i.next();
            if (mapEntry.getKey() != null && mapEntry.getValue() instanceof List) {
                List list = (List) mapEntry.getValue();
                if (list.size() > 0) {
                    String key = mapEntry.getKey().toString();
                    List values = ((List) mapEntry.getValue());
                    if (key.equals(COOKIES)) {
                        for (Object o : values) {
                            String value = o.toString();
                            for (String s : value.split("; ")) {
                                String[] data = s.split("=");
                                if (data.length == 2)
                                    responseWrapper.cookies.put(data[0], data[1]);
                                else {
                                    LOGGER.warning(data[0]);
                                }
                            }
                        }
                    } else {
                        responseWrapper.headers.put(key, values.get(0).toString());
                    }

                }
            }
        }
//        Debugger.dumpMap(responseWrapper.headers);
//        Debugger.dumpMap(responseWrapper.cookies);
        return responseWrapper;
    }

    private static String getEncoding(String contentType) {
        contentType = contentType.toLowerCase();
        if (contentType.contains(CHAR_SET)) {
            int startEncoding = contentType.indexOf(CHAR_SET) + CHAR_SET.length();
            int endEncoding = contentType.indexOf(';', startEncoding) > startEncoding ? contentType.indexOf(';', startEncoding) : contentType.length();
            return contentType.substring(startEncoding, endEncoding);
        } else {
            return null;
        }
    }

    public void writeContent(HttpServletResponse response, Transformer transformer) throws IOException {
        response.setStatus(statusCode);
        response.setContentType(contentType);
        response.setCharacterEncoding(encoding);

        for (String cookieKey : cookies.keySet()) {
            if (!RESERVED_TOKENS.contains(cookieKey)) {
                try {
                    Cookie cookie = new Cookie(cookieKey, cookies.get(cookieKey));
                    if (cookies.get(cookieKey).equals("deleted")) {
                        cookie.setMaxAge(0);
                    } else {
                        cookie.setMaxAge(14 * 24 * 60 * 60);
                    }
                    response.addCookie(cookie);
                } catch (Exception ex) {
                    LOGGER.warning(ex.getMessage());
                }

            }
        }
        for (String header : headers.keySet()) {
            if (header.equalsIgnoreCase(LOCATION) || headers.get(header).startsWith("http")) {
                response.setHeader(header, localPath + "?url=" + headers.get(header));
            } else {
                response.setHeader(header, headers.get(header));
            }
        }
        if (transformer != null) {
            IOUtils.write(transformer.transformContent(encoding, content), response.getOutputStream());
        } else {
            IOUtils.write(content, response.getOutputStream());
        }
    }

    public void writeContent(HttpServletResponse response) throws IOException {
        writeContent(response, null);
    }

    public int getStatusCode() {
        return statusCode;
    }

    public Map<String, String> getHeaders() {
        return headers;
    }

    public byte[] getContent() {
        return content;
    }

    public String getContentType() {
        return contentType;
    }

    public String getEncoding() {
        return encoding;
    }
}
